//
//  ZXLSignTools.m
//  SignSecurityDemo
//
//  Created by zxl02 on 2020/8/14.
//  Copyright © 2020 CordovaDemo. All rights reserved.
//

#import "ZXLSignTools.h"

#pragma mark- Tools
@implementation ZXLSignTools

// 字典key排序
+ (NSString *)getNeedSignStrFrom:(id)obj dataType:(DictionaryType)dataType securityResult:(SecurityResult)securityResult{
     NSDictionary *dict = obj;
     NSArray *arrPrimary;
     NSArray *arrPrimary2=dict.allKeys;
    if (dataType==DictionaryTypeKey) {
        arrPrimary=dict.allKeys;
    }else{
        arrPrimary=dict.allValues;
    }
    NSArray *arrKey = [arrPrimary sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2){
         NSComparisonResult result = [obj1 compare:obj2];
        if (securityResult==SecurityReusltAscending) {
            
            return result==NSOrderedAscending;//NSOrderedAscending 倒序
        }else{
            return result==NSOrderedDescending;//NSOrderedAscending 倒序
        }
     }];
     NSString*str =@"";
    if (dataType==DictionaryTypeKey) {
        str = [arrKey componentsJoinedByString:@"&"];
      }else{
          NSArray *arrKey = [arrPrimary2 sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2){
              NSComparisonResult result = [obj1 compare:obj2];
              if (securityResult==SecurityReusltAscending) {
                  return result==NSOrderedAscending;//NSOrderedAscending 倒序
              }else{
                  return result==NSOrderedDescending;//NSOrderedAscending 倒序
              }
          }];
        for (NSString *s in arrKey) {
            id value = dict[s];
            if([value isKindOfClass:[NSDictionary class]]) {
                value = [self getNeedSignStrFrom:value];
            }
            if([str length] !=0) {
                str = [str stringByAppendingString:@"&"];
            }
            str = [str stringByAppendingFormat:@"%@",value];
        }
    }
     return str;
}
+ (NSString *)getNeedSignStrFrom:(id)obj{
    NSDictionary *dict = obj;
    NSArray *arrPrimary = dict.allKeys;
    
    NSArray *arrKey = [arrPrimary sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2){
        NSComparisonResult result = [obj1 compare:obj2];
        return result==NSOrderedAscending;//NSOrderedAscending 倒序
    }];
    
    NSString*str =@"";
    for (NSString *s in arrKey) {
        id value = dict[s];
        if([value isKindOfClass:[NSDictionary class]]) {
            value = [self getNeedSignStrFrom:value];
        }
        if([str length] !=0) {
            
            str = [str stringByAppendingString:@"&"];
            
        }
        str = [str stringByAppendingFormat:@"%@",value];
    }
    
    return str;
}
+(NSString *)getStringWithDic:(NSMutableDictionary*)params{
    NSArray *keyArray = [params allKeys];//获取待排序的key
    NSArray *sortArray = [keyArray sortedArrayUsingComparator:^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2) {
        return [obj1 compare:obj2 ];
    }];//获取排序后的key
    //排序后的以key=value拼接的数组
    NSMutableArray *valueArray = [NSMutableArray array];
    for(NSString *sortSring in sortArray){
        NSString *signSring = [NSString stringWithFormat:@"%@=%@",sortSring,[params objectForKey:sortSring]];
        [valueArray addObject:signSring];
    }
    // 就是用“&”把每个排序后拼接的数组，用字符串拼接起来
    NSString *string = [valueArray componentsJoinedByString:@"&"];
    return string;
}

+(NSString *)JSONString:(NSDictionary*)dic{
    NSError *error = nil;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic
                                                       options:NSJSONWritingPrettyPrinted
                                                         error:&error];
    if (jsonData == nil) {
#ifdef DEBUG
        NSLog(@"fail to get JSON from dictionary: %@, error: %@", dic, error);
#endif
        return nil;
    }
    NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    return jsonString;
}
+(NSDictionary *) dictionaryValue:(NSString*)jsonStr{
    NSError *errorJson;
    NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:[jsonStr dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&errorJson];
    if (errorJson != nil) {
#ifdef DEBUG
        NSLog(@"fail to get dictioanry from JSON: %@, error: %@", self, errorJson);
#endif
    }
    return jsonDict;
}

+(NSString *)base64EncodeString:(NSString *)string{
    //1、先转换成二进制数据
    NSData *data =[string dataUsingEncoding:NSUTF8StringEncoding];
    //2、对二进制数据进行base64编码，完成后返回字符串
    return [data base64EncodedStringWithOptions:0];
}

+(NSString *)base64DecodeString:(NSString *)string{
    //注意：该字符串是base64编码后的字符串
    //1、转换为二进制数据（完成了解码的过程）
    NSData *data=[[NSData alloc]initWithBase64EncodedString:string options:0];
    //2、把二进制数据转换成字符串
    return [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
}
+(NSData *)base64DataFromString:(NSString *)string {
    unsigned long ixtext, lentext;
    unsigned char ch, inbuf[4], outbuf[3];
    short i, ixinbuf;
    Boolean flignore, flendtext = false;
    const unsigned char *tempcstring;
    NSMutableData *theData;
    
    if (string == nil) {
        return [NSData data];
    }
    
    ixtext = 0;
    
    tempcstring = (const unsigned char *)[string UTF8String];
    
    lentext = [string length];
    
    theData = [NSMutableData dataWithCapacity: lentext];
    
    ixinbuf = 0;
    
    while (true) {
        if (ixtext >= lentext) {
            break;
        }
        
        ch = tempcstring [ixtext++];
        
        flignore = false;
        
        if ((ch >= 'A') && (ch <= 'Z')) {
            ch = ch - 'A';
        }
        else if ((ch >= 'a') && (ch <= 'z')) {
            ch = ch - 'a' + 26;
        }
        else if ((ch >= '0') && (ch <= '9')) {
            ch = ch - '0' + 52;
        }
        else if (ch == '+') {
            ch = 62;
        }
        else if (ch == '=') {
            flendtext = true;
        }
        else if (ch == '/') {
            ch = 63;
        }
        else {
            flignore = true;
        }
        
        if (!flignore) {
            short ctcharsinbuf = 3;
            Boolean flbreak = false;
            
            if (flendtext) {
                if (ixinbuf == 0) {
                    break;
                }
                
                if ((ixinbuf == 1) || (ixinbuf == 2)) {
                    ctcharsinbuf = 1;
                }
                else {
                    ctcharsinbuf = 2;
                }
                
                ixinbuf = 3;
                
                flbreak = true;
            }
            
            inbuf [ixinbuf++] = ch;
            
            if (ixinbuf == 4) {
                ixinbuf = 0;
                
                outbuf[0] = (inbuf[0] << 2) | ((inbuf[1] & 0x30) >> 4);
                outbuf[1] = ((inbuf[1] & 0x0F) << 4) | ((inbuf[2] & 0x3C) >> 2);
                outbuf[2] = ((inbuf[2] & 0x03) << 6) | (inbuf[3] & 0x3F);
                
                for (i = 0; i < ctcharsinbuf; i++) {
                    [theData appendBytes: &outbuf[i] length: 1];
                }
            }
            if (flbreak) {
                break;
            }
        }
    }
    return theData;
}
+ (NSString *)underlineFromCamel:(NSString*)str
{
    if (str.length == 0) return str;
     NSMutableString *string = [NSMutableString string];
     for (NSUInteger i = 0; i<str.length; i++) {
         unichar c = [str characterAtIndex:i];
         NSString *cString = [NSString stringWithFormat:@"%c", c];
         NSString *cStringLower = [cString lowercaseString];
         if ([cString isEqualToString:cStringLower]) {
             [string appendString:cStringLower];
         } else {
             [string appendString:@"_"];
             [string appendString:cStringLower];
         }
     }
     return string;
}

+ (NSString *)camelFromUnderline:(NSString*)str;
{
    if (str.length == 0) return str;
    NSMutableString *string = [NSMutableString string];
    NSArray *cmps = [str componentsSeparatedByString:@"_"];
    for (NSUInteger i = 0; i<cmps.count; i++) {
        NSString *cmp = cmps[i];
        if (i && cmp.length) {
            [string appendString:[NSString stringWithFormat:@"%c", [cmp characterAtIndex:0]].uppercaseString];
            if (cmp.length >= 2) [string appendString:[cmp substringFromIndex:1]];
        } else {
            [string appendString:cmp];
        }
    }
    return string;
}

+ (NSString *)firstCharUpper:(NSString*)str;
{
    if (str.length == 0) return str;
    NSMutableString *string = [NSMutableString string];
    [string appendString:[NSString stringWithFormat:@"%c", [str characterAtIndex:0]].lowercaseString];
    if (str.length >= 2) [string appendString:[str substringFromIndex:1]];
    return string;
}

+ (NSString *)firstCharLower:(NSString*)str
{
    if (str.length == 0) return str;
    NSMutableString *string = [NSMutableString string];
    [string appendString:[NSString stringWithFormat:@"%c", [str characterAtIndex:0]].uppercaseString];
    if (str.length >= 2) [string appendString:[str substringFromIndex:1]];
    return string;
}
+ (NSData *)UTF8DataWith:(NSData*)data
{
    //保存结果
    NSMutableData *resData = [[NSMutableData alloc] initWithCapacity:data.length];
    
    //无效编码替代符号(常见 � □ ?)
    NSData *replacement = [@"�" dataUsingEncoding:NSUTF8StringEncoding];
    
    uint64_t index = 0;
    const uint8_t *bytes = data.bytes;
    
    while (index < data.length)
    {
        uint8_t len = 0;
        uint8_t header = bytes[index];
        
        //单字节
        if ((header&0x80) == 0)
        {
            len = 1;
        }
        //2字节(并且不能为C0,C1)
        else if ((header&0xE0) == 0xC0)
        {
            if (header != 0xC0 && header != 0xC1)
            {
                len = 2;
            }
        }
        //3字节
        else if((header&0xF0) == 0xE0)
        {
            len = 3;
        }
        //4字节(并且不能为F5,F6,F7)
        else if ((header&0xF8) == 0xF0)
        {
            if (header != 0xF5 && header != 0xF6 && header != 0xF7)
            {
                len = 4;
            }
        }
        
        //无法识别
        if (len == 0)
        {
            [resData appendData:replacement];
            index++;
            continue;
        }
        
        //检测有效的数据长度(后面还有多少个10xxxxxx这样的字节)
        uint8_t validLen = 1;
        while (validLen < len && index+validLen < data.length)
        {
            if ((bytes[index+validLen] & 0xC0) != 0x80)
                break;
            validLen++;
        }
        
        //有效字节等于编码要求的字节数表示合法,否则不合法
        if (validLen == len)
        {
            [resData appendBytes:bytes+index length:len];
        }else
        {
            [resData appendData:replacement];
        }
        
        //移动下标
        index += validLen;
    }
    
    return resData;
}
@end

/*
#pragma mark - AES
@implementation ZXLSignToolAES
//size_t const kKeySize = kCCKeySizeAES256;
NSString *const kInitVector = @"ixohjDEnchErAvfP";
static char base64EncodingTable[64] = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
};
+(NSString *)AESEncrypt:(NSString *)plainText key:(NSString *)key keySize:(SecuritySignLengthAES)keySize{
    size_t const kKeySize = keySize;
    char keyPtr[kKeySize +1 ];
       memset(keyPtr, 0, sizeof(keyPtr));
       [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
       
       NSData* data = [plainText dataUsingEncoding:NSUTF8StringEncoding];
       NSUInteger dataLength = [data length];
    
       NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];

       size_t bufferSize = dataLength + kCCBlockSizeAES128;
       unsigned char *buffer = malloc(bufferSize);
       size_t numBytesEncrypted = 0;
        
       CCCryptorStatus cryptStatus = CCCrypt(kCCEncrypt,
                                             kCCAlgorithmAES128,
                                             kCCOptionPKCS7Padding,
                                             keyPtr,
                                             kKeySize,
                                             initVector.bytes,
                                             [data bytes],
                                             dataLength,
                                             buffer,
                                             bufferSize,
                                             &numBytesEncrypted);
       if (cryptStatus == kCCSuccess) {
           NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted];
           return [self base64StringFromData:resultData length:[resultData length]];
//           return [[NSData dataWithBytesNoCopy:buffer length:numBytesEncrypted] base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
       }
       free(buffer);
       return @"";
}
+(NSString *)AESDecrypt:(NSString *)encryptText key:(NSString *)key keySize:(SecuritySignLengthAES)keySize{
    size_t const kKeySize = keySize;
    char keyPtr[kKeySize + 1];
       memset(keyPtr, 0, sizeof(keyPtr));
       [key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding];
       //NSData *data = [GTMBase64 decodeData:[encryptText dataUsingEncoding:NSUTF8StringEncoding]];
       NSData *data=[ZXLSignTools base64DataFromString:encryptText];
       NSUInteger dataLength = [data length];
       size_t bufferSize = dataLength + kCCBlockSizeAES128;
       unsigned char *buffer = malloc(bufferSize);
       NSData *initVector = [kInitVector dataUsingEncoding:NSUTF8StringEncoding];

       size_t numBytesCrypted = 0;
       CCCryptorStatus cryptStatus = CCCrypt(kCCDecrypt,
                                             kCCAlgorithmAES128,
                                             kCCOptionPKCS7Padding,
                                             keyPtr,
                                             kKeySize,
                                             initVector.bytes,
                                             [data bytes],
                                             dataLength,
                                             buffer,
                                             bufferSize,
                                             &numBytesCrypted);
       if (cryptStatus == kCCSuccess) {
           NSData *resultData = [NSData dataWithBytesNoCopy:buffer length:numBytesCrypted];
           return [[NSString alloc] initWithData:resultData encoding:NSUTF8StringEncoding];
       }
       free(buffer);
       return @"";
}
+(NSString *)base64StringFromData: (NSData *)data length: (NSUInteger)length {
    unsigned long ixtext, lentext;
    long ctremaining;
    unsigned char input[3], output[4];
    short i, charsonline = 0, ctcopy;
    const unsigned char *raw;
    NSMutableString *result;
    
    lentext = [data length];
    if (lentext < 1) {
        return @"";
    }
    result = [NSMutableString stringWithCapacity: lentext];
    raw = [data bytes];
    ixtext = 0;
    
    while (true) {
        ctremaining = lentext - ixtext;
        if (ctremaining <= 0) {
            break;
        }
        for (i = 0; i < 3; i++) {
            unsigned long ix = ixtext + i;
            if (ix < lentext) {
                input[i] = raw[ix];
            }
            else {
                input[i] = 0;
            }
        }
        output[0] = (input[0] & 0xFC) >> 2;
        output[1] = ((input[0] & 0x03) << 4) | ((input[1] & 0xF0) >> 4);
        output[2] = ((input[1] & 0x0F) << 2) | ((input[2] & 0xC0) >> 6);
        output[3] = input[2] & 0x3F;
        ctcopy = 4;
        switch (ctremaining) {
            case 1:
                ctcopy = 2;
                break;
            case 2:
                ctcopy = 3;
                break;
        }
        
        for (i = 0; i < ctcopy; i++) {
            [result appendString: [NSString stringWithFormat: @"%c", base64EncodingTable[output[i]]]];
        }
        
        for (i = ctcopy; i < 4; i++) {
            [result appendString: @"="];
        }
        
        ixtext += 3;
        charsonline += 4;
        
        if ((length > 0) && (charsonline >= length)) {
            charsonline = 0;
        }
    }
    return result;
}
@end
*/
